package com.zte.claa.inficombo.csa.app.controller;

import java.io.UnsupportedEncodingException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import com.zte.claa.inficombo.csa.app.controller.ret.CmccOneNetRptData;
import com.zte.claa.inficombo.csa.app.service.nbiot.NBIotDevDataRptService;
import com.zte.claa.inficombo.csa.app.util.CmccOneNetUtil;
import com.zte.claa.inficombo.csa.app.util.CodeSafe;
import com.zte.claa.inficombo.csa.app.util.JsonUtils;

/**
 * 中移OneNet平台应用数据推送接收Controller类:
 *  URL:  http://[inficombo-csa-ip]:8098/claa/csa/cmcc/onenet/appdatarpt
 *
 * Created by Roy on 2017/5/17.
 * Modify by Lee on 2020/01/28.
 * 
 * @Controller
 * @EnableAutoConfiguration
 */
@RestController
@RequestMapping("/claa/csa/cmcc/onenet") 
public class CmccOnetRptController extends BaseController {
	private Logger logger = LoggerFactory.getLogger(CmccOnetRptController.class);
	
	// 用户自定义token和OneNet第三方平台配置里的token一致
	@Value("${csa.cmcc.onenet.rpt.token}")
    private String token ="";
    
    //aeskey和OneNet第三方平台配置里的apikey一致
    @Value("${csa.cmcc.onenet.rpt.aeskey}")
    private String aeskey ="";
    
    // 数据推送是否加密模式
    @Value("${csa.cmcc.onenet.rpt.encryptflag}")
    private boolean encryptFlag = false;
    
    @Autowired
    private NBIotDevDataRptService rptService;
    
    /**
     * 功能说明： URL&Token验证接口。如果验证成功返回msg的值，否则返回其他值。
     * @param msg 验证消息
     * @param nonce 随机串
     * @param signature 签名
     * @return msg值
     */
    @RequestMapping(value = "/appdatarpt", method = RequestMethod.GET)
    @ResponseBody
    public String check(@RequestParam(value = "msg") String msg, @RequestParam(value = "nonce") String nonce,
                        @RequestParam(value = "signature") String signature) throws UnsupportedEncodingException 
    {
        logger.info("url&token check: msg={} nonce={} signature={}",msg,nonce,signature);
    	msg = CodeSafe.xssFilter(msg);
    	nonce = CodeSafe.xssFilter(nonce);
    	signature = CodeSafe.xssFilter(signature);
        if (CmccOneNetUtil.checkToken(msg,nonce,signature,this.token) == false){
            logger.warn("checkToken failed!");
        }
        return msg;
    }
    
    /**
     * 功能描述：第三方平台数据接收。<p>
     *   <ul>注:
     *      <li>1.OneNet平台为了保证数据不丢失，有重发机制，如果重复数据对业务有影响，数据接收端需要对重复数据进行排除重复处理。</li>
     *      <li>2.OneNet每一次post数据请求后，等待客户端的响应都设有时限，在规定时限内没有收到响应会认为发送失败。
     *            接收程序接收到数据时，尽量先缓存起来，再做业务逻辑处理。</li>
     *    </ul>
     * @param body 数据消息
     * @return 任意字符串。OneNet平台接收到http 200的响应，才会认为数据推送成功，否则会重发。
     * 
     *【注】：
     * GET:  url验证；
     * POST: 数据推送；
     */
    @RequestMapping(value = "/appdatarpt", method=RequestMethod.POST)
    @ResponseBody
    public String receive(@RequestBody String body) throws Exception {
        logger.info("data receive body String: " +body);
        int ret = 0;
		try {
			if (this.encryptFlag) {
				ret = this.processRptEncryptMsg(body);
			} else {
				ret = this.processRptPlainMsg(body);
			}
		}catch(Exception e){
        	logger.error("appdatarpt process failed."+e.getMessage(), e);
        	ret = 1;
        }        
        if(ret == 0){
        	return "ok";
        } else {
        	return "error";
        }
    }
    
    /**
     * 处理明文上报消息
     * 
     * @param msgBody
     * @return
     */
    private int processRptPlainMsg(String msgBody) throws Exception {
        CmccOneNetUtil.BodyObj obj = CmccOneNetUtil.resolveBody(msgBody, false);
        logger.info("[Plainmsg] data receive:  body Object --- " +obj);
        int ret = 0;
        if (obj != null){
            boolean dataRight = CmccOneNetUtil.checkSignature(obj, this.token);
            if (dataRight == false){
                logger.warn("Rpt data signature error: "+obj.toString());
            }
            Object msgObj = obj.getMsg();
            String jsonMsg = (msgObj == null) ? null : msgObj.toString();
            this.parseOnenetRptData(jsonMsg);
            ret = 0;
        }else {
            logger.info("data receive: body empty error");
            ret = 1;
        }
    	return ret;
    }
    
    /**
     * 处理密文上报消息
     * 
     * @param msgBody
     * @return
     */
    private int processRptEncryptMsg(String msgBody) throws Exception {
    	CmccOneNetUtil.BodyObj obj1 = CmccOneNetUtil.resolveBody(msgBody, true);
        logger.info("[Encryptmsg] data receive:  body Object--- " +obj1);
        int ret = 0;
        if (obj1 != null){
            boolean dataRight = CmccOneNetUtil.checkSignature(obj1, this.token);
            if (dataRight == false){
                logger.warn("[Encrypt] Rpt data signature error: "+obj1.toString());
            }
            String msg = CmccOneNetUtil.decryptMsg(obj1, this.aeskey);
            logger.info("data receive: content" + msg);
            this.parseOnenetRptData(msg);
            ret = 0;
        }else {
            logger.info("[Encrypt]data receive: body empty error" );
            ret = 1;
        }
    	return ret;
    }
    
    /**
     * 解析onenet上报的json（msg）内容.
     * @param jsonMsg: json对象或json数组（配置数据缓存的场景）；
     * 
	 	字段		类型		字段说明
		type	int		标识消息类型, 1：设备上传数据点消息; 2：设备上下线消息; 7：缓存命令下发后结果上报（仅支持NB设备）
		dev_id	int		设备ID
		ds_id	string	数据流名称
		at		int		平台时间戳,单位ms
		value	具体数据部分，为设备上传至平台或触发的相关数据，详见说明：
		status	int		设备上下线标识； 0：设备下线； 1：设备上线；
		login_type	int	设备登录协议类型， 1-EDP, 6-MODBUS, 7-MQTT, 10-NB-IoT
		cmd_type	int	命令响应的类型， 1：设备收到cmd的ACK响应信息； 2：设备收到cmd的Confirm响应信息
		cmd_id	string	命令ID。 
		... ... 更多参见： https://open.iot.10086.cn/doc/book/application-develop/httppush/develop-manual.html 
	【说明】：当响应结果包含二进制数据时，是将二进制数据byte[] 转换成 ASCII 码的数组存在JSON数据中，如: [98, 105, 110, 97, 114, 121]
     */
    public void parseOnenetRptData(String jsonMsg){
    	if(jsonMsg == null){
    		return;
    	}
    	try{
    		CmccOneNetRptData d = JsonUtils.readValue(jsonMsg, CmccOneNetRptData.class);
    		if(d != null){
    			if(d.getType() == CmccOneNetRptData.MSG_TYPE_CMDACK){
    				d.setOriginMsg(jsonMsg);
    			}
    			logger.info("[ok]: "+d.toString());
    			this.rptService.submitCmccOneNetRptData(d);
    		} else {
    			// 尝试按CmccOneNetRptData[]数组读取一次
    			CmccOneNetRptData[] dArr = JsonUtils.readValue(jsonMsg, CmccOneNetRptData[].class);
    			if(dArr != null && dArr.length > 0){
    				for(int i=0; i<dArr.length; i++){
    					logger.info("["+i+"]: "+dArr[i].toString());
    					this.rptService.submitCmccOneNetRptData(dArr[i]);
    				}
    			}
    		}    		
    	}catch(Exception e){
    		logger.error("parseOnenetRptData failed: {}", jsonMsg, e);
    	}    	
    }

    // test 
    public static void main2(String[] args){
    	String s1 = "{\"type\": 1, \"dev_id\": 2016617, \"ds_id\": \"datastream_id\", \"at\": 1466133706841,\"value\": 42}";
    	String s2 = "[{"
    			+ "\"type\": 1,"
    			+ "\"dev_id\": 2016618,"
    			+ "\"ds_id\": \"datastream_id\","
    			+ "\"at\": 1466133726841,"
    			+ "\"value\": 42"
    			+ "},"
    			+ "{"
    			+ "\"type\": 1,"
    			+ "\"dev_id\": 2016619,"
    			+ "\"ds_id\": \"datastream_id\","
    			+ "\"at\": 1466133736842,"
    			+ "\"value\": 43"
    			+ "}]";
        	
    	String s3 = "{\"type\": 1, \"dev_id\": 2016620, \"ds_id\": \"datastream_id\", \"at\": 1466133706841,\"value\": [42,43,46,69]}";
    	String s4 = "{"
    			+ "\"type\": 2,"
    			+ "\"dev_id\": 2016620,"
    			+ "\"status\": 0,"
    			+ "\"login_type\": 1,"
    			+ "\"at\": 1466133706841"
    			+ "}";
    	
    	String s5 = "{"
    			+ "\"type\": 7,"
    			+ "\"cmd_id\": \"3a351323-c4fe-5f21-9e9e-a9adc321182f\","
    			+ "\"imei\": \"865820060031939\","
    			+ "\"dev_id\": 2016690,"
    			+ "\"cmd_type\": 0,"
    			+ "\"send_time\": 1466133706841,"
    			+ "\"send_status\": 5,"
    			+ "\"confirm_time\": 146613371921,"
    			+ "\"confirm_status\": 0,"
    			+ "\"confirm_body\": {"
    			+ "	\"obj_id\": 3,"
    			+ "	\"obj_inst\": [{"
    			+ "		\"obj_inst_id\": 0,"
    			+ "		\"res\": [{"
    			+ "			\"res_inst\": [{"
    			+ "				\"val\": 0,"
    			+ "				\"res_inst_id\": 0"
    			+ "			}],"
    			+ "			\"res_id\": 11"
    			+ "			},"
    			+ "			{"
    			+ "				\"val\": 1530496927000,"
    			+ "				\"res_id\": 13"
    			+ "			}]"
    			+ "	}]"
    			+ "}"
    			+ "}";
    	
    	CmccOnetRptController c = new CmccOnetRptController();
    	System.out.println("------s1--------------");
    	c.parseOnenetRptData(s1);
    	System.out.println("------s2--------------");
    	c.parseOnenetRptData(s2);
    	System.out.println("------s3--------------");
    	c.parseOnenetRptData(s3);
    	System.out.println("------s4--------------");
    	c.parseOnenetRptData(s4);
    	System.out.println("------s5--------------");
    	c.parseOnenetRptData(s5);
    }
}
